package com.yunhe.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhe.gateway.constant.ErrorCode;
import com.yunhe.common.constant.SystemConsts;
import com.yunhe.common.model.ResultObject;
import com.yunhe.gateway.service.TokenService;
import com.yunhe.common.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Map;

/**
 * 认证过滤器
 * @author liuronglei
 */
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
    @Autowired
    private TokenService tokenService;

    @Override
    public int getOrder() {
        //数字越低优先级越高
        return -200;
    }

    public static String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String ipAddress = getIpAddress(request);
        if(!StringUtil.isEmpty(ipAddress)){
            request= request.mutate().header(SystemConsts.HEADER.REMOTE_ADDRESS.value(), ipAddress).build();
        }
        //cors 跨域401 问题
        if(request.getMethod().equals(RequestMethod.OPTIONS.name())){
            response.setStatusCode(HttpStatus.OK);
            ServerWebExchange build = exchange.mutate().request(request).build();
            return chain.filter(build);
        }
        URI requestURI = request.getURI();
        String path = requestURI.getPath();
        String token;
        ObjectMapper mapper = new ObjectMapper();
        // 暂时不进行权限过滤
        if (path.startsWith("/yh-authority/login") ||path.startsWith("/yh-authority/appLogin")
                || (path.startsWith("/yh-authority/users") && path.endsWith("/forgetPassword"))
                || path.startsWith("/yh-authority/users/byName")
                || path.startsWith("/yh-authority/users/verificationCode")
                || (path.startsWith("/yh-option/file/getFile"))
                || (path.startsWith("/yh-authority/wechat/getParkByPosition"))
                || (path.startsWith("/yh-authority/wechat/login"))
                || (path.startsWith("/yh-authority/wechat/loginByVerificationCode"))
                || (path.startsWith("/yh-authority/wechat/changePassword"))
                || (path.startsWith("/yh-authority/wechat/findMenusByParkId"))
                || (path.startsWith("/yh-portal/portal/findWeChatNews"))
                || (path.startsWith("/yh-portal/portal/findWeChatNotice"))
                || (path.startsWith("/yh-portal/portal/findWeChatBanner"))
                || (path.startsWith("/yh-authority/backLogin"))
                || (path.startsWith("/yh-common") && path.endsWith("/image") )) {
            return chain.filter(exchange);
        } else if (path.endsWith("/socket")) {
            // 从url参数中取出token
            token = request.getQueryParams().getFirst(SystemConsts.HEADER.TOKEN.value());
        } else {
            // 从http请求头中取出token
            token = request.getHeaders().getFirst(SystemConsts.HEADER.TOKEN.value());
        }
        if (StringUtil.isEmpty(token)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().set("Content-Type", "application/json");
            byte[] result = null;
            try {
                result = mapper.writeValueAsBytes(new ResultObject<>(ErrorCode.CODE.LOGIN_EXPIRE.value(), "token lost"));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            DataBuffer buffer = response.bufferFactory().wrap(result);
            return response.writeWith(Flux.just(buffer));
        }
        // 验证token
        Map<String, String> valueMap = tokenService.parseToken(token);
        if (valueMap == null) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().set("Content-Type", "application/json");
            byte[] result = null;
            try {
                result = mapper.writeValueAsBytes(new ResultObject<>(ErrorCode.CODE.LOGIN_EXPIRE.value(), "token invalid"));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            DataBuffer buffer = response.bufferFactory().wrap(result);
            return response.writeWith(Flux.just(buffer));
        }
        String userId = valueMap.get(SystemConsts.HEADER.USER_ID.value());
        String firmId = valueMap.get(SystemConsts.HEADER.FIRM_ID.value());
        String roleId = valueMap.get(SystemConsts.HEADER.ROLE_ID.value());
        String stationId = valueMap.get(SystemConsts.HEADER.STATION_ID.value());
        if (!StringUtil.isEmpty(userId)) {
            request = request.mutate().header(SystemConsts.HEADER.USER_ID.value(), valueMap.get(SystemConsts.HEADER.USER_ID.value())).build();
        }
        if (!StringUtil.isEmpty(firmId)) {
            request = request.mutate().header(SystemConsts.HEADER.FIRM_ID.value(), valueMap.get(SystemConsts.HEADER.FIRM_ID.value())).build();
        }
        if (!StringUtil.isEmpty(roleId)) {
            request = request.mutate().header(SystemConsts.HEADER.ROLE_ID.value(), valueMap.get(SystemConsts.HEADER.ROLE_ID.value())).build();
        }
        if (!StringUtil.isEmpty(stationId)) {
            request = request.mutate().header(SystemConsts.HEADER.STATION_ID.value(), valueMap.get(SystemConsts.HEADER.STATION_ID.value())).build();
        }
        // 判断用户、单位、角色是否已删除或已失效

        ServerWebExchange build = exchange.mutate().request(request).build();
        return chain.filter(build);
    }
//
//    @Override
//    public boolean shouldFilter() {
//        RequestContext ctx = RequestContext.getCurrentContext();
//        HttpServletRequest request = ctx.getRequest();
//        String requestURI = request.getRequestURI().toLowerCase();
//        String contextPath = request.getContextPath();
//        if (requestURI.startsWith(contextPath + "/authority") && requestURI.endsWith("/login")) {
//            return false;
//        } else if (requestURI.endsWith("v2/api-docs")) {
//            return false;
//        }
//        return true;
//    }
}